Skip to content

fix(antminer): read power draw from the new_api stats payload - #325

Merged
b-rowan merged 4 commits into
256foundation:masterfrom
dmgblockchain:pr/antminer-wattage
Aug 19, 2026
Merged

fix(antminer): read power draw from the new_api stats payload#325
b-rowan merged 4 commits into
256foundation:masterfrom
dmgblockchain:pr/antminer-wattage

Conversation

@cryptographicturk

@cryptographicturk cryptographicturk commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Depends on #328. That PR fixes the RPC transport so new_api actually
reaches the miner. Until it merges, its commit appears here too; afterwards
this PR is the single mod.rs change. Reworked from the web endpoint to RPC
per review.

wattage is always None on newer stock firmware. DataField::Wattage is
sourced from the legacy RPC stats payload, which carries no power reading on
these generations.

Where the reading lives

The new_api variant of stats reports it. That is a different payload to the
legacy one — its STATS array holds a single element, so the value sits at
/STATS/0 rather than the /STATS/1 the legacy source uses:

{"command":"stats"}                  -> STATS len 2, Msg "CGMiner stats", no power
{"command":"stats","new_api":true}   -> STATS len 1, Msg "stats",         power/watt present

The change

  • Adds stats + new_api as a second source for DataField::Wattage, read at
    /STATS/0.
  • Accepts the watt key alongside the existing power / Power /
    chain_power. The spelling varies by model on identical firmware: the L9
    reports power, the L11 watt.

The legacy location is kept and tried first, so firmware that does report power
there is unaffected. Applied to both v2020 and v2023_07.

Measured

Against live hardware, all previously None:

L11   3651 W        L9   3357 W
L11   3636 W        L9   3353 W
                    L9   3379 W

An idle unit reports its true low draw rather than falling back to absent, so a
genuine zero stays distinguishable from a missing reading.

No regressions in the groups that cannot benefit: T21, S21 Hydro and S21+ Hydro
expose no power draw on any transport and continue to report nothing, and older
units that do not honour new_api still receive the legacy payload exactly as
before.

The two payloads share only fan_num and rate_30m, identical in both, so
merging them into one field cannot silently clobber a value.

Verification

  • cargo fmt --all -- --check — clean
  • cargo clippy -p asic-rs-firmwares-antminer --all-targets — clean
  • cargo test -p asic-rs-firmwares-antminer — 20 passed
  • cargo test --workspace — no failures
  • Live check against multiple L9 and L11 units

@b-rowan b-rowan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think using the RPC endpoint is preferable (it should be faster than the web and doesn't need auth).

Comment thread asic-rs-firmwares/antminer/src/backends/v2020/mod.rs Outdated
…meter`

`send_rpc_command` wrapped every parameter value in cgminer's `parameter`
argument. Bitmain's `new_api` is not a `parameter` — it is a top-level flag
alongside `command`, and the firmware ignores it when nested, answering with
the legacy payload instead.

Every `new_api` helper on this client was affected: `stats`, `summary`,
`pools`, `rate`, `warning` and `reload` all requested the new API and silently
received the old one. The failure is invisible to a caller — the response is a
well-formed `STATUS: "S"` body, just the wrong shape.

Verified against L9 and L11 on 86.48-2.0.0:

    {"command":"stats","new_api":true}                -> STATS[1], "stats"
    {"command":"stats","parameter":{"new_api":true}}  -> STATS[2], "CGMiner stats"

The second is byte-identical to sending no parameter at all.

Object parameters now merge at the top level; scalars keep the `parameter`
wrapper, preserving cgminer's own convention for commands like `switchpool`
that take a pool index. Within this backend every parameter is currently a
`new_api` object, so nothing relied on the old wrapping.

Request construction is extracted into `build_rpc_request` so the wire format
is unit-testable without a socket.
`wattage` is always `None` on newer stock firmware. `DataField::Wattage` is
sourced from the legacy RPC `stats` payload, which carries no power reading on
these generations.

The reading is present in the `new_api` variant of `stats`. That is a
different payload: its `STATS` array holds a single element, so the value sits
at `/STATS/0` rather than the `/STATS/1` the legacy source uses. The legacy
location is kept and tried first, so firmware that does report power there is
unaffected.

The key name varies by model on identical firmware — the L9 reports `power`,
the L11 `watt` — so `watt` is accepted alongside the existing `power` /
`Power` / `chain_power`.

Measured against live hardware:

    L11   None -> 3651 W        L9   None -> 3357 W
    L11   None -> 3636 W        L9   None -> 3353 W
                                L9   None -> 3379 W

An idle unit reports its true low draw rather than falling back to absent, so
a genuine zero stays distinguishable from a missing reading. Models whose
firmware exposes no power at all (T21, S21 Hydro, S21+ Hydro) continue to
report nothing, and units that do not honour `new_api` still receive the
legacy payload.

Applied to both `v2020` and `v2023_07`.
@cryptographicturk cryptographicturk changed the title fix(antminer): read power draw from the web stats endpoint fix(antminer): read power draw from the new_api stats payload Aug 13, 2026
@cryptographicturk

Copy link
Copy Markdown
Contributor Author

I updated it after submitting another PR to fix an underlying issue that prevented this solution from working.

@b-rowan b-rowan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nits only, just shorten a comment. Contingent on #328, small fixes there then I will merge both.

Comment thread asic-rs-firmwares/antminer/src/backends/v2020/mod.rs Outdated
Comment thread asic-rs-firmwares/antminer/src/backends/v2020/rpc.rs
…hape

Review feedback: check for the `new_api` key directly instead of inferring
top-level placement from the parameter being a JSON object.

`build_rpc_request` now removes `new_api` from the parameter object by name
and places it alongside `command`. Every other parameter — object or scalar —
keeps cgminer's `parameter` wrapper, so the generic object rule no longer
applies to payloads that never needed it.

The check stays inside `build_rpc_request` because `new_api` does not only
arrive through the typed helpers on this client: `APIClient::get_api_result`
forwards the parameters of a `MinerCommand::RPC` straight through, which is
the path a `GetDataLocations` entry takes. A dedicated `flags` argument on the
RPC command would be the place for further Bitmain extensions, but it means
changing `MinerCommand` in core, so it is left for when a second flag exists.

Tests renamed to describe the key rather than the shape, and two added: a
non-`new_api` object keeps the wrapper, and `new_api` is lifted out of a
larger object while the remaining keys stay wrapped.
Review feedback: the payload difference is implied by the flag itself, so the
explanation of why the reading sits at `/STATS/0` is not needed. The note on
why the command cannot be a `const` stays.
@b-rowan
b-rowan merged commit 9e163e6 into 256foundation:master Aug 19, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants